package nkw;

import robocode.HitByBulletEvent;
import robocode.HitRobotEvent;
import robocode.HitWallEvent;
import robocode.Rules;
import robocode.ScannedRobotEvent;

/**
 * Competitive Robot.
 * 
 * @author Noah Woodden <noah.woodden@gmail.com>
 * 
 */
public class CompetitiveRobot extends RobotBase {

  /**
   * The maximum distance for the robot to travel ahead per turn.
   */
  private double aheadDistanceLimit = 400;

  /**
   * The maximum allowed distance from robot to enable firing.
   */
  private double doNotFireDistanceLimit = 200;

  /**
   * The maximum rotation in degrees allowed to enable firing when turning to face enemy.
   */
  private double doNotFireTurnLimit = 60;

  /**
   * Enable the onHitByBullet event.
   */
  private boolean useOnBulletEvent = false;

  /**
   * Enable the use of change position tracking. Helpful for when multiple on event handlers are
   * called and position of robot changes unexpectedly.
   */
  private boolean useOnChangePosition = false;

  /**
   * Current status of changed position.
   */
  private boolean onChangedPosition = false;

  /**
   * Current status describing whether an on event handler is being executed or not.
   */
  private boolean processingEvent = false;

  /**
   * Runs robot.
   */
  @Override
  public void run() {
    while (true) {
      // If nothing else is happening turn radar to scan for enemies.
      this.turnRadarRight(360);
    }
  }

  /**
   * Turns and moves the robot away from the wall when the robot hits it.
   * 
   * @param event Event handler.
   */
  @Override
  public void onHitWall(HitWallEvent event) {
    this.onChangedPosition = this.processingEvent = true;

    // Turn and move away from the wall.
    this.turnRight(-1 * event.getBearing());
    this.ahead(this.aheadDistanceLimit);
    this.processingEvent = false;
  }

  /**
   * When a robot is scanned, the robot aligns gun with target first, since this is faster than
   * aligning the entire robot, and attempts to fire in succession. After firing, the robot is
   * re-aligned and travels in the direction of the target. This event handler does not execute
   * internal code if another handler is currently executing. In other words, this handler has lower
   * priority than the others.
   * 
   * @param event Event handler.
   */
  @Override
  public void onScannedRobot(ScannedRobotEvent event) {
    if (!this.processingEvent) {
      this.onChangedPosition = false;

      double distance = event.getDistance();

      // Turn gun towards target.
      double toTurn = event.getBearing() - (this.getGunHeading() - this.getHeading());
      this.turnGunRight(toTurn);

      // Robot moved by other event, no longer aligned with target so no need to fire.
      if (this.useOnChangePosition && this.onChangedPosition) {
        this.onChangedPosition = false;

        this.alignBodyToGun();
      }
      // If within distance and turn limits, try to fire multiple rounds.
      else if (distance <= this.doNotFireDistanceLimit) {
        if (toTurn <= (this.doNotFireTurnLimit / (distance / 200))) {
          double power = Rules.MAX_BULLET_POWER / (distance / 100);
          this.fire(power);
          /*if (this.getEnergy() > 70) {
            this.fire(power / 2);
          }
          if (this.getEnergy() > 40) {
            this.fire(power / 4);
          }*/
        }
        this.alignBodyToGun();
        this.ahead(Math.min(this.aheadDistanceLimit, distance / 1.3));
      }
      // Couldn't fire, so just realign and move so that next on-scan may be more fruitful.
      else {
        this.alignBodyToGun();
        this.ahead(Math.min(this.aheadDistanceLimit, distance / 1.3));
      }
    }
  }

  /**
   * When hit by a bullet, move robot in a direction away from the bullet's source as quickly as
   * possible.
   * 
   * @param event Event handler.
   */
  @Override
  public void onHitByBullet(HitByBulletEvent event) {
    if (this.useOnBulletEvent) {
      this.onChangedPosition = this.processingEvent = true;

      double bearing = event.getBearing();

      // Turn and move away from bullet in the closest direction possible.
      if (bearing < 0) {
        if (bearing > -45) {
          this.turnRight(-45 - bearing);
        }
      }
      else {
        if (bearing < 45) {
          this.turnRight(45 - bearing);
        }
      }
      this.ahead(100);

      this.processingEvent = false;
    }
  }

  /**
   * When colliding with a robot, quickly turn gun and fire at full power since likeliness of
   * hitting robot is much greater at such close distance.
   * 
   * @param event Event handler.
   */
  @Override
  public void onHitRobot(HitRobotEvent event) {
    this.onChangedPosition = this.processingEvent = true;

    double toTurn = event.getBearing() - (this.getGunHeading() - this.getHeading());

    this.turnGunRight(toTurn);
    // If within turn limits, try to fire at maximum power.
    if (toTurn <= this.doNotFireTurnLimit) {
      this.fire(Rules.MAX_BULLET_POWER);
    }

    this.processingEvent = false;
  }
}
